/*
 * Copyright (c) 2016 Vivid Solutions.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * and Eclipse Distribution License v. 1.0 which accompanies this distribution.
 * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html
 * and the Eclipse Distribution License is available at
 *
 * http://www.eclipse.org/org/documents/edl-v10.php.
 */
package org.locationtech.jts.operation.buffer;

import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryCollection;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.LineString;
import org.locationtech.jts.geom.LinearRing;
import org.locationtech.jts.geom.Polygon;
import org.locationtech.jts.geom.PrecisionModel;

import test.jts.GeometryTestCase;



/**
 * @version 1.7
 */
public class BufferTest extends GeometryTestCase {

  public BufferTest(String name) {
    super(name);
  }

  public static void main(String[] args) {
    junit.textui.TestRunner.run(BufferTest.class);
  }

  public void testFirst() throws Exception
  {
    testMultiLineString_separateBuffers_floatingSingle();
  }
  
  /**
   * This tests that the algorithm used to generate fillet curves
   * does not suffer from numeric "slop-over".
   * See GEOS PR #282.
   */
  public void testPointBufferSegmentCount() {
    Geometry g = read("POINT ( 100 100 )");
    checkPointBufferSegmentCount(g, 80, 53);
    checkPointBufferSegmentCount(g, 80, 129);
  }
  
  private void checkPointBufferSegmentCount(Geometry g, double dist, int quadSegs) {
    Geometry buf = g.buffer(dist, quadSegs);
    int segsExpected = 4 * quadSegs + 1;
    assertEquals(segsExpected, buf.getNumPoints());
  }

  public void testMultiLineString_depthFailure() throws Exception {
    new BufferValidator(
      15,
      "MULTILINESTRING ((1335558.59524 631743.01449, 1335572.28215 631775.89056, 1335573.2578018496 631782.1915185435),  (1335573.2578018496 631782.1915185435, 1335576.62035 631803.90754), (1335558.59524 631743.01449, 1335573.2578018496 631782.1915185435), (1335573.2578018496 631782.1915185435, 1335580.70187 631802.08139))")
      .setEmptyBufferExpected(false)
      .test();
  }
  public void testMultiLineString_separateBuffers_floating() throws Exception {
    new BufferValidator(
      0.01,
      "MULTILINESTRING (( 635074.5418406526 6184832.4888257105, 635074.5681951842 6184832.571842485, 635074.6472587794 6184832.575795664 ), ( 635074.6657069515 6184832.53889932, 635074.6933792098 6184832.451929366, 635074.5642420045 6184832.474330718 ))")
      .setBufferHolesExpected(false)
      .setEmptyBufferExpected(false)
      .test();
  }
  public void testMultiLineString2_buffersTouchToMakeHole_floating() throws Exception {
    new BufferValidator(
      0.037,
      "MULTILINESTRING (( 635074.5418406526 6184832.4888257105, 635074.5681951842 6184832.571842485, 635074.6472587794 6184832.575795664 ), ( 635074.6657069515 6184832.53889932, 635074.6933792098 6184832.451929366, 635074.5642420045 6184832.474330718 ))")
      .setBufferHolesExpected(true)
      .setEmptyBufferExpected(false)
      .test();
  }
  public void testMultiLineString3_holeVanishes_floating() throws Exception {
    new BufferValidator(
      0.16,
      "MULTILINESTRING (( 635074.5418406526 6184832.4888257105, 635074.5681951842 6184832.571842485, 635074.6472587794 6184832.575795664 ), ( 635074.6657069515 6184832.53889932, 635074.6933792098 6184832.451929366, 635074.5642420045 6184832.474330718 ))")
      .setBufferHolesExpected(false)
      .setEmptyBufferExpected(false)
      .test();
  }
  public void testMultiLineString4_reallyBigDistance_floating() throws Exception {
    new BufferValidator(
      1E10,
      "MULTILINESTRING (( 635074.5418406526 6184832.4888257105, 635074.5681951842 6184832.571842485, 635074.6472587794 6184832.575795664 ), ( 635074.6657069515 6184832.53889932, 635074.6933792098 6184832.451929366, 635074.5642420045 6184832.474330718 ))")
      .setBufferHolesExpected(false)
      .setEmptyBufferExpected(false)
      .test();
  }

  public void testMultiLineString_separateBuffers_floatingSingle() throws Exception {
    BufferValidator bv = new BufferValidator(
      0.01,
      "MULTILINESTRING (( 635074.5418406526 6184832.4888257105, 635074.5681951842 6184832.571842485, 635074.6472587794 6184832.575795664 ), ( 635074.6657069515 6184832.53889932, 635074.6933792098 6184832.451929366, 635074.5642420045 6184832.474330718 ))",
      false);
    
      bv.setBufferHolesExpected(false);
      bv.setEmptyBufferExpected(true);
      bv.setPrecisionModel(new PrecisionModel(PrecisionModel.FLOATING_SINGLE));
      bv.test();
  }
  
  public void testMultiLineString2_buffersTouchToMakeHole_floatingSingle() throws Exception {
    new BufferValidator(
      0.037,
      "MULTILINESTRING (( 635074.5418406526 6184832.4888257105, 635074.5681951842 6184832.571842485, 635074.6472587794 6184832.575795664 ), ( 635074.6657069515 6184832.53889932, 635074.6933792098 6184832.451929366, 635074.5642420045 6184832.474330718 ))",
      false)
      .setBufferHolesExpected(false)
      .setEmptyBufferExpected(true)
      .setPrecisionModel(new PrecisionModel(PrecisionModel.FLOATING_SINGLE))
      .test();
  }
  public void testMultiLineString3_holeVanishes_floatingSingle() throws Exception {
    new BufferValidator(
      0.16,
      "MULTILINESTRING (( 635074.5418406526 6184832.4888257105, 635074.5681951842 6184832.571842485, 635074.6472587794 6184832.575795664 ), ( 635074.6657069515 6184832.53889932, 635074.6933792098 6184832.451929366, 635074.5642420045 6184832.474330718 ))",
      false)
      .setBufferHolesExpected(false)
      .setEmptyBufferExpected(true)
      .setPrecisionModel(new PrecisionModel(PrecisionModel.FLOATING_SINGLE))
      .test();
  }
  public void testMultiLineString4_reallyBigDistance_floatingSingle() throws Exception {
    new BufferValidator(
      1E10,
      "MULTILINESTRING (( 635074.5418406526 6184832.4888257105, 635074.5681951842 6184832.571842485, 635074.6472587794 6184832.575795664 ), ( 635074.6657069515 6184832.53889932, 635074.6933792098 6184832.451929366, 635074.5642420045 6184832.474330718 ))")
      .setBufferHolesExpected(false)
      .setEmptyBufferExpected(false)
      .setPrecisionModel(new PrecisionModel(PrecisionModel.FLOATING_SINGLE))
      .test();
  }
  public void testPolygon_MultipleHoles() throws Exception {
    new BufferValidator(
      10.0,
      "POLYGON (( 78 82, 78 282, 312 282, 312 82, 78 82 ), ( 117 242, 122 242, 122 248, 117 248, 117 242 ), ( 156 104, 288 104, 288 210, 156 210, 156 104 ))")
      .setBufferHolesExpected(true)
      .setEmptyBufferExpected(false)
      .setPrecisionModel(new PrecisionModel(PrecisionModel.FLOATING))
      .test();
  }

  public void test1() throws Exception {
    new BufferValidator(
      0,
      "POINT (100 100)")
      .setEmptyBufferExpected(true)
      .test();
  }
  public void test2() throws Exception {
    new BufferValidator(
      0,
      "LINESTRING (10 10, 100 100)")
      .setEmptyBufferExpected(true)
      .test();
  }
  public void test1a() throws Exception {
    new BufferValidator(
      -1,
      "POINT (100 100)")
      .setEmptyBufferExpected(true)
      .test();
  }
  public void test2a() throws Exception {
    new BufferValidator(
      -1,
      "LINESTRING (10 10, 100 100)")
      .setEmptyBufferExpected(true)
      .test();
  }

  public void test3() throws Exception {
    new BufferValidator(
      10,
      "LINESTRING (100 100, 200 100, 200 200, 100 200, 100 100)")
      .test();
  }

  public void test4() throws Exception {
    new BufferValidator(
      50,
      "LINESTRING (40 40, 160 40, 100 180, 40 80)")
      .test();
  }
  public void test5() throws Exception {
    new BufferValidator(
      0,
      "POLYGON ((80 300, 280 300, 280 300, 280 300, 280 80, 80 80, 80 300))")
      .test();
  }
  public void test6() throws Exception {
    new BufferValidator(
      10,
      "POLYGON ((60 300, 60 160, 240 160, 240 300, 60 300))")
      .test();
  }
  public void test7() throws Exception {
    new BufferValidator(
      10,
      "POLYGON ((80 300, 280 300, 280 80, 80 80, 80 300), (260 280, 180 200, 100 280, 100 100, 260 100, 260 280))")
      .test();
  }
  public void test8() throws Exception {
    new BufferValidator(
      200,
      "POLYGON ((80 300, 280 300, 280 80, 80 80, 80 300), (260 280, 180 200, 100 280, 100 100, 260 100, 260 280))")
      .test();
  }
  public void test9() throws Exception {
    new BufferValidator(
      -10,
      "POLYGON ((80 300, 280 300, 280 80, 80 80, 80 300))")
      .test();
  }
  public void test10() throws Exception {
    new BufferValidator(
      10,
      "POLYGON ((100 300, 300 300, 300 100, 100 100, 100 300), (220 220, 180 220, 180 180, 220 180, 220 220))")
      .test();
  }
  public void test11() throws Exception {
    new BufferValidator(
      5,
      "POLYGON ((260 400, 220 300, 80 300, 180 220, 40 200, 180 160, 60 20, 200 80, 280 20, 260 140, 440 20, 340 180, 520 160, 280 220, 460 340, 300 300, 260 400), (260 320, 240 260, 220 220, 160 180, 220 160, 200 100, 260 160, 300 140, 320 180, 260 200, 260 320))")
      .test();
  }
  public void test12() throws Exception {
    new BufferValidator(
      -17,
      "POLYGON ((260 320, 240 260, 220 220, 160 180, 220 160, 260 160, 260 200, 260 320))")
      .test();
  }
  public void test13() throws Exception {
    new BufferValidator(
      -17,
      "POLYGON ((260 320, 240 260, 220 220, 260 160, 260 320))")
      .test();
  }
  public void test14() throws Exception {
    new BufferValidator(
      -14,
      "POLYGON ((260 320, 240 260, 220 220, 260 160, 260 320))")
      .test();
  }
  public void test15() throws Exception {
    new BufferValidator(
      26,
      "LINESTRING (260 160, 260 200, 260 320, 240 260, 220 220)")
      .test();
  }
  public void test16() throws Exception {
    new BufferValidator(
      -7,
      "POLYGON ((260 400, 220 300, 80 300, 180 220, 40 200, 180 160, 60 20, 200 80, 280 20, 260 140, 440 20, 340 180, 520 160, 280 220, 460 340, 300 300, 260 400), (260 320, 240 260, 220 220, 160 180, 220 160, 200 100, 260 160, 300 140, 320 180, 260 200, 260 320))")
      .test();
  }
  public void test17() throws Exception {
    new BufferValidator(
      -183,
      "POLYGON ((32 136, 27 163, 30 236, 34 252, 49 291, 72 326, 83 339, 116 369, 155 391, 176 400, 219 414, 264 417, 279 416, 339 401, 353 395, 380 381, 394 372, 441 328, 458 303, 463 294, 480 251, 486 205, 486 183, 473 115, 469 105, 460 85, 454 74, 423 33, 382 2, 373 -3, 336 -19, 319 -24, 275 -31, 252 -32, 203 -27, 190 -24, 149 -10, 139 -5, 84 37, 76 46, 52 81, 36 121, 32 136))")
      .test();
  }
  public void test18() throws Exception {
    new BufferValidator(
      20,
      "POLYGON((-4 225, -17 221, -16 223, -15 224, -13 227, -4 225))")
      .test();
  }
  public void test19() throws Exception {
    new BufferValidator(
      21,
      "POLYGON ((184 369, 181 368, 180 368, 179 367, 176 366, 185 357, 184 369 ))")
      .test();
  }
  public void test20() throws Exception {
    new BufferValidator(
      1000,
      "POLYGON ((13841 1031, 13851 903, 13853 885, 13853 875, 13856 862, 13859 831, 13670 900, 13841 1031))")
      .test();
  }
  public void test21() throws Exception {
    new BufferValidator(
      18,
      "POLYGON ((164 84, 185 91, 190 75, 187 76, 182 77, 179 79, 178 79, 174 81, 173 81, 172 82, 169 83,  164 84 ))")
      .test();
  }
  public void test22() throws Exception {
    new BufferValidator(
      15,
      "POLYGON ((224 271, 225 261, 214 258, 210 266, 212 267, 214 267, 217 268, 218 268, 219 268, 221 269, 222 270,  224 271 ))")
      .test();
  }
  public void test23() throws Exception {
    new BufferValidator(
      25,
      "POLYGON ((484 76, 474 79, 492 122, 502 119, 501 117, 500 112, 499 111, 498 107, 497 104, 496 103, 494 98, 493 96, 491 92, 490 90, 489 86, 487 81, 486 79, 485 77, 484 76 ))")
      .test();
  }
  public void test24() throws Exception {
    new BufferValidator(
      160,
      "POLYGON ((20 60, 20 20, 240 20, 40 21, 240 22, 40 22, 240 23, 240 60, 20 60))")
      .test();
  }
  public void test25() throws Exception {
    new BufferValidator(
      -3,
      "POLYGON ((233 195, 232 195, 231 194, 222 188, 226 187, 227 187, 229 187, 230 186, 232 186, 234 185, 236 184, 237 183, 238 182, 237 184, 236 185, 236 186, 235 187, 235 188, 234 189, 234 191, 234 192, 233 193, 233 195 ))")
      .test();
  }
  public void test26() throws Exception {
    new BufferValidator(
      6,
      "LINESTRING (233 195, 232 195, 231 194, 222 188, 226 187, 227 187, 229 187, 230 186, 232 186, 234 185, 236 184, 237 183, 238 182, 237 184, 236 185, 236 186, 235 187, 235 188, 234 189, 234 191, 234 192, 233 193, 233 195 )")
      .test();
  }
  public void test27() throws Exception {
    new BufferValidator(
      -30,
      "POLYGON ((2330 1950, 2320 1950, 2310 1940, 2220 1880, 2260 1870, 2270 1870, 2290 1870, 2300 1860, 2320 1860, 2340 1850, 2360 1840, 2370 1830, 2380 1820, 2370 1840, 2360 1850, 2360 1860, 2350 1870, 2350 1880, 2340 1890, 2340 1910, 2340 1920, 2330 1930, 2330 1950 ))")
      .test();
  }
  public void test28() throws Exception {
    new BufferValidator(
      30,
      "LINESTRING (2330 1950, 2320 1950, 2310 1940, 2220 1880, 2260 1870, 2270 1870, 2290 1870, 2300 1860, 2320 1860, 2340 1850, 2360 1840, 2370 1830, 2380 1820, 2370 1840, 2360 1850, 2360 1860, 2350 1870, 2350 1880, 2340 1890, 2340 1910, 2340 1920, 2330 1930, 2330 1950 )")
      .test();
  }
  public void test29() throws Exception {
    new BufferValidator(
      26,
      "POLYGON ((440 -93, 440 -67, 475 -67, 471 -71, 469 -72, 468 -73, 467 -74, 463 -78, 459 -81, 458 -82, 454 -84, 453 -85, 452 -86, 450 -86, 449 -87, 448 -88, 444 -90, 443 -91, 441 -92, 440 -93 ))")
      .test();
  }
  public void test30() throws Exception {
    new BufferValidator(
      260,
      "POLYGON ((4400 -930, 4400 -670, 4750 -670, 4710 -710, 4690 -720, 4680 -730, 4670 -740, 4630 -780, 4590 -810, 4580 -820, 4540 -840, 4530 -850, 4520 -860, 4500 -860, 4490 -870, 4480 -880, 4440 -900, 4430 -910, 4410 -920, 4400 -930 ))")
      .test();
  }
  public void test31() throws Exception {
    new BufferValidator(
      0.1,
      "POLYGON ((635074.6769928858 6184832.427381967, 635075.6723193424 6184799.950949265, 634717.5983159657 6184655.107092909, 634701.0176852546 6184648.498845058, 634697.7188197445 6184647.20632975, 634694.416887708 6184645.922033237, 634691.1138635761 6184644.642692243, 634687.8077729489 6184643.371570057, 634684.498667351 6184642.107006015, 634681.1875340013 6184640.847368483, 634677.8742698929 6184639.595978798, 634674.5570551592 6184638.351118257, 634671.2386969016 6184637.112873929, 634667.9173237421 6184635.881187774, 634664.5938713895 6184634.656088823, 634661.2674041622 6184633.437548058, 634657.9388577675 6184632.2255945075, 634654.6082322216 6184631.02022817, 634651.2745403448 6184629.823080709, 634647.9388208436 6184628.630859804, 634644.6000865338 6184627.4451971175, 634641.2592216335 6184626.267782336, 634637.9163291481 6184625.095294129, 634634.5713061031 6184623.931053837, 634631.2232683088 6184622.773371783, 634636.1918816608 6184608.365992378, 634633.2495506873 6184607.353869728, 634630.3051410569 6184606.348333739, 634627.3587557608 6184605.346063082, 634624.4102918282 6184604.3503790945, 634621.4607364619 6184603.359650123, 634618.5091539674 6184602.37384716, 634615.5564800596 6184601.392999219, 634612.6017790422 6184600.417077295, 634609.6450509242 6184599.446081388, 634606.6862442375 6184598.481672177, 634603.7263976521 6184597.52055733, 634600.7654082242 6184596.566058185, 634597.80145603 6184595.61645607, 634594.8364124894 6184594.671808995, 634591.8702261405 6184593.733777636, 634588.9020642313 6184592.799011653, 634585.9318238292 6184591.870832384, 634582.960543591 6184590.945947501, 634579.9871848791 6184590.027649342, 634577.0127348808 6184589.11430624, 634574.0362578988 6184588.205889201, 634571.0586381858 6184587.304087893, 634568.0790429743 6184586.405551985, 634565.0983050519 6184585.513631817, 634562.115540186 6184584.626637723, 634559.1316840936 6184583.744598703, 634556.1458010782 6184582.867485766, 634553.1587753976 6184581.996988578, 634550.1697742692 6184581.12975681, 634547.1796305005 6184580.269140797, 634544.1874598458 6184579.41345088, 634541.194198027 6184578.562716054, 634538.1998450506 6184577.716936321, 634535.2034137691 6184576.877743362, 634532.2059428038 6184576.041844833, 634529.2063935531 6184575.212533085, 634526.2057531879 6184574.388176442, 634523.2040217179 6184573.568774906, 634520.2002119992 6184572.755960159, 634517.1953626422 6184571.946439856, 634514.1893707667 6184571.143535337, 634510.267712847 6184585.871039091, 634281.9449709259 6184525.076957544, 633860.4859191478 6184412.861324424, 633664.3557212166 6184360.639468017, 633645.5884675509 6184355.641948889, 633486.222 6184313.208, 633485.7474265156 6184328.852301474, 633485.2749953512 6184344.496113185, 633650.4562371405 6184388.478170839, 633669.5206846121 6184393.553017912, 633852.6461183216 6184442.312440121, 634280.9949861752 6184556.364455, 634502.4254528129 6184615.324425217, 634505.716566367 6184616.204307566, 634509.0065372197 6184617.090806118, 634512.2953653594 6184617.983920872, 634515.5812308139 6184618.88193318, 634518.8659020835 6184619.788222348, 634522.1484948951 6184620.7010987215, 634525.4299963829 6184621.61893061, 634528.7093679372 6184622.545010364, 634531.9867124417 6184623.476016638, 634535.2619784358 6184624.413610098, 634538.5360501477 6184625.3594804, 634541.807159074 6184626.310248219, 634545.0771251463 6184627.267632202, 634548.3459483465 6184628.231632348, 634551.611808724 6184629.200529998, 634554.8764747474 6184630.177704472, 634558.138126462 6184631.161437107, 634561.3986867118 6184632.150125222, 634564.6571168633 6184633.147061154, 634567.9135198268 6184634.14892356, 634571.1678441261 6184635.157373106, 634574.421025444 6184636.172438785, 634577.6711923747 6184637.194062597, 634580.9192805979 6184638.222273537, 634584.1662257991 6184639.257100599, 634587.410208043 6184640.296825108, 634590.6529957657 6184641.3448264, 634593.8928205046 6184642.397725132, 634597.131502168 6184643.457239971, 634600.3671178726 6184644.524973575, 634603.6016934284 6184645.596001944, 634605.6958877691 6184646.2958191885, 634606.946276627 6184646.713661825, 634608.6177147877 6184647.275847967, 634610.2887808911 6184647.8379089665, 634613.6292576884 6184648.967082693, 634616.9666683461 6184650.104475331, 634620.3029357173 6184651.248484221, 634623.6361369188 6184652.4007120095, 634626.9663749042 6184653.557837355, 634630.2954695637 6184654.721578941, 634633.6214980055 6184655.893539405, 634636.9454988878 6184657.070426424, 634640.2664335228 6184658.255532312, 634643.5852890809 6184659.44722541, 634646.9020655481 6184660.6455057105, 634650.2167629072 6184661.850373212, 634653.5284454564 6184663.061798892, 634656.8370616816 6184664.2814434115, 634660.1436502574 6184665.506014449, 634663.4481081718 6184666.7388333315, 634666.7496027217 6184667.976549702, 634670.0489665787 6184669.222513908, 634673.3453155413 6184670.475036258, 634676.6396367943 6184671.732485097, 634679.9308916207 6184672.9981527375, 634683.2200672035 6184674.270407524, 634686.5062278372 6184675.54922043, 634689.789322 6184676.8362521175, 634693.0703883899 6184678.128210268, 634696.3493754807 6184679.426755545, 634699.6253475712 6184680.731858918, 634702.8982531315 6184682.04518105, 634706.1681951834 6184683.363400595, 635074.6769928858 6184832.427381967))")
      .test();
  }
  public void test32() throws Exception {
    new BufferValidator(
      30,
      "MULTILINESTRING ((80 285, 85.5939933259177 234.65406006674084 ), (85.5939933259177 234.65406006674084, 98 123, 294 92, 344.3694502052736 126.0884157954882 ), (344.3694502052736 126.0884157954882, 393 159 ), (51 235, 85.5939933259177 234.65406006674084 ), (85.5939933259177 234.65406006674084, 251 233, 344.3694502052736 126.0884157954882 ), (344.3694502052736 126.0884157954882, 382 83 ))")
      .test();
  }
  public void test33() throws Exception {
    //Get side location conflict in #contains, but the geometry is invalid
    // [Jon Aquino 10/29/2003]
    //    new BufferValidator(.0001, new
	// PrecisionModel(PrecisionModel.FLOATING_SINGLE), "MULTIPOLYGON
	// (((708258.754920656
    // 2402197.91172757, 708257.029447455 2402206.56901508, 708652.961095455
    // 2402312.65463437, 708657.068786251 2402304.6356364, 708258.754920656
    // 2402197.91172757 )), ((708653.498611049 2402311.54647056,
    // 708708.895756966 2402203.47250014, 708280.326454234 2402089.6337791,
    // 708247.896591321 2402252.48269854, 708367.379593851 2402324.00761653,
    // 708248.882609455 2402253.07294874, 708249.523621829 2402244.3124463,
    // 708261.854734465 2402182.39086576, 708262.818392579 2402183.35452387,
    // 708653.498611049 2402311.54647056 )))")
    //      .test();
  }
  public void test34() throws Exception {
    new BufferValidator(
      1,
      "GEOMETRYCOLLECTION (POLYGON ((0 10, 10 0, 10 10, 0 10),  (4 8, 8 4, 8 8, 4 8)),   LINESTRING (6 6, 20 20))")
      .test();
  }
  public void test35() throws Exception {
    new BufferValidator(
      20,
      "GEOMETRYCOLLECTION (POINT (100 100), POLYGON ((400 260, 280 380, 240 220, 120 300, 120 100, 260 40, 200 160, 400 260)), LINESTRING (260 400, 220 280, 120 400, 20 280, 160 160, 60 40, 160 20, 360 140))")
      .test();
  }
  public void test36() throws Exception {
    new BufferValidator(
      20,
      "GEOMETRYCOLLECTION (POINT (100 100), POLYGON ((400 260, 120 300, 120 100, 400 260)), LINESTRING (20 280, 160 160, 60 40))")
      .test();
  }
  public void test37() throws Exception {
    new BufferValidator(
      300,
      "POLYGON ((-140 700, 880 1120, 1280 -120, 300 -600, -480 -480, -140 700),   (0 360, 780 500, 240 -220, 0 360))")
      .test();
  }
  public void test38() throws Exception {
    new BufferValidator(
      300,
      "POLYGON ((-140 700, 880 1120, 1280 -120, 300 -600, -480 -480, -140 700),   (0 360, 240 -220, 780 500, 0 360))")
      .test();
  }
  public void test39() throws Exception {
    new BufferValidator(
      30,
      "MULTIPOLYGON (((0 400, 440 400, 440 0, 0 0, 0 400),(380 360, 160 120, 260 80, 380 360)), ((360 320, 200 120, 240 100, 360 320)))")
      .test();
  }

  public void testFloatingPrecision1() throws Exception {
    new BufferValidator(
      100,
      "LINESTRING (331771 5530174, 331776 5530175, 331782 5530177, 331787 5530177, 331791 5530178, 331796 5530178, 331800 5530178, 331805 5530177, 331811 5530176, 331817 5530175, 331823 5530173, 331828 5530171, 331832 5530169, 331835 5530167, 331839 5530163, 331843 5530160, 331846 5530157, 331849 5530154, 331853 5530150, 331855 5530145, 331857 5530141)")
      .test();
  }
  public void testFloatingPrecision2() throws Exception {
    new BufferValidator(
      100,
      "LINESTRING (317091 5557033, 317079 5557042, 317067 5557053, 317055 5557065, 317045 5557078, 317037 5557091, 317029 5557098, 317016 5557108, 317002 5557118, 316990 5557129, 316986 5557131, 316978 5557133, 316968 5557133, 316965 5557131, 316954 5557120, 316952 5557115, 316951 5557108, 316949 5557092, 316948 5557076, 316946 5557063, 316944 5557057, 316937 5557042, 316924 5557029, 316911 5557019, 316896 5557009, 316881 5557001, 316865 5556997, 316849 5556992, 316834 5556988, 316817 5556985, 316801 5556983, 316766 5556983, 316751 5556982, 316733 5556980, 316716 5556976, 316702 5556968, 316699 5556964, 316691 5556951, 316680 5556934, 316670 5556922, 316660 5556911, 316642 5556885, 316637 5556881)")
      .test();
  }
  public void testFloatingPrecision3() throws Exception {
    new BufferValidator(
      100,
      "LINESTRING (300181 5547255, 300183 5547252, 300203 5547253, 300209 5547261, 300237 5547277, 300262 5547286, 300280 5547292, 300288 5547297, 300293 5547303, 300297 5547311, 300299 5547319, 300299 5547334, 300306 5547349, 300320 5547367)")
      .test();
  }
  public void testFloatingPrecision4() throws Exception {
    new BufferValidator(
      100,
      "LINESTRING (301546 5537924, 301547 5537922, 301551 5537919, 301555 5537919, 301559 5537918, 301565 5537918, 301569 5537917, 301573 5537915, 301580 5537912, 301583 5537909, 301587 5537906, 301594 5537900, 301598 5537897, 301601 5537893, 301605 5537889, 301608 5537885, 301609 5537880, 301612 5537876, 301614 5537873, 301616 5537869, 301620 5537865, 301624 5537860, 301632 5537852, 301640 5537842, 301643 5537836, 301644 5537829, 301644 5537822, 301646 5537815, 301647 5537808, 301650 5537802, 301650 5537796, 301651 5537791, 301653 5537786, 301654 5537780, 301656 5537773, 301658 5537767, 301662 5537761)")
      .test();
  }
  public void testFloatingPrecision5() throws Exception {
    new BufferValidator(
      100,
      "LINESTRING (334797 5560136, 334781 5560129, 334777 5560128, 334762 5560122, 334760 5560121, 334752 5560116, 334745 5560109, 334742 5560103, 334741 5560098, 334736 5560087, 334731 5560082, 334726 5560081, 334708 5560072, 334691 5560063, 334674 5560052, 334660 5560048, 334655 5560048, 334633 5560049, 334621 5560046, 334616 5560043, 334596 5560034, 334586 5560025, 334573 5560009, 334562 5559982, 334549 5559943, 334543 5559923, 334538 5559905, 334535 5559887, 334530 5559869, 334536 5559853)")
      .test();
  }
  public void testFloatingPrecision6() throws Exception {
    new BufferValidator(
      100,
      "LINESTRING (316640 5563099, 316639 5563114, 316642 5563132, 316644 5563137, 316650 5563144, 316653 5563147, 316663 5563159, 316665 5563164, 316667 5563172, 316667 5563193, 316668 5563209, 316672 5563214, 316678 5563228, 316679 5563230, 316679 5563236, 316678 5563252, 316676 5563256, 316671 5563270, 316669 5563289, 316667 5563304, 316666 5563308, 316656 5563323, 316646 5563336, 316639 5563347)")
      .test();
  }
  public void testFloatingPrecision7() throws Exception {
    new BufferValidator(
      100,
      "LINESTRING (301178 5534835, 301189 5534837, 301218 5534837, 301229 5534836, 301237 5534836, 301245 5534838, 301268 5534838, 301273 5534837, 301279 5534838, 301286 5534838, 301289 5534839, 301296 5534842, 301302 5534844, 301306 5534846, 301309 5534850, 301313 5534853, 301316 5534856, 301319 5534868, 301320 5534873, 301323 5534877, 301326 5534882, 301334 5534896, 301340 5534902, 301344 5534908, 301348 5534913, 301352 5534919, 301357 5534925, 301363 5534932, 301369 5534937, 301375 5534941, 301382 5534949, 301386 5534955, 301397 5534964, 301402 5534967, 301407 5534972, 301411 5534975, 301414 5534980, 301418 5534986, 301419 5534989, 301422 5534994, 301426 5535000, 301438 5535012, 301444 5535017, 301456 5535030, 301457 5535033)")
      .test();
  }
  public void testFloatingPrecision8() throws Exception {
    new BufferValidator(
      100,
      "LINESTRING (303722 5533544, 303713 5533542, 303706 5533539, 303697 5533537, 303694 5533534, 303677 5533527, 303673 5533525, 303670 5533524, 303669 5533523, 303664 5533519, 303654 5533513, 303647 5533507, 303644 5533506, 303634 5533504, 303633 5533504, 303627 5533502)")
      .test();
  }
  public void testFloatingPrecision9() throws Exception {
    new BufferValidator(
      100,
      "LINESTRING (309969 5563538, 309955 5563542, 309941 5563548, 309913 5563562, 309896 5563569, 309883 5563576, 309868 5563586, 309855 5563594, 309841 5563603, 309830 5563614, 309818 5563624, 309805 5563635, 309791 5563645, 309778 5563654, 309766 5563663, 309752 5563672, 309722 5563692, 309709 5563699, 309681 5563713, 309667 5563721, 309651 5563728, 309631 5563734, 309615 5563739, 309602 5563747, 309589 5563756, 309578 5563766, 309566 5563775, 309554 5563785, 309542 5563796, 309538 5563801, 309535 5563810, 309532 5563828, 309533 5563833, 309540 5563855, 309546 5563868, 309552 5563884, 309556 5563900, 309559 5563916, 309561 5563933, 309561 5563970, 309559 5563988, 309554 5564003, 309550 5564018, 309546 5564032, 309542 5564047, 309538 5564061, 309531 5564074, 309528 5564077, 309518 5564090, 309507 5564101, 309493 5564110, 309492 5564111, 309480 5564119, 309474 5564121, 309458 5564123, 309443 5564125, 309426 5564125, 309408 5564123, 309393 5564123, 309377 5564126, 309373 5564129, 309364 5564139, 309360 5564147, 309359 5564151, 309359 5564155, 309362 5564159)")
      .test();
  }
  public void testFloatingPrecision10() throws Exception {
    new BufferValidator(
      100,
      "LINESTRING (299331 5536963, 299335 5536956, 299335 5536953, 299336 5536949, 299338 5536942, 299345 5536933, 299349 5536927, 299352 5536924, 299358 5536922, 299364 5536919, 299369 5536916, 299375 5536912, 299380 5536908, 299387 5536905, 299391 5536904, 299395 5536902, 299399 5536899, 299402 5536896, 299405 5536892, 299415 5536886, 299425 5536882, 299435 5536880, 299449 5536874, 299455 5536869, 299461 5536865, 299468 5536862, 299474 5536859, 299480 5536855, 299491 5536846, 299497 5536842, 299502 5536838, 299508 5536835, 299513 5536834, 299523 5536830, 299527 5536826, 299532 5536824, 299542 5536821, 299548 5536818, 299552 5536814, 299556 5536812, 299566 5536808, 299574 5536804, 299588 5536798, 299594 5536796, 299602 5536792, 299611 5536789, 299625 5536784, 299632 5536782, 299648 5536780, 299657 5536785, 299672 5536798, 299678 5536801, 299688 5536801, 299696 5536802, 299711 5536807, 299729 5536807, 299739 5536808, 299748 5536807, 299766 5536810, 299777 5536813, 299801 5536816, 299825 5536811, 299850 5536810, 299867 5536809, 299875 5536811, 299887 5536811, 299895 5536815, 299913 5536822)")
      .test();
  }
  public void testFloatingPrecision11() throws Exception {
    new BufferValidator(
      100,
      "LINESTRING (325089 5534737, 325089 5534733, 325093 5534723, 325099 5534718, 325118 5534712, 325129 5534709, 325147 5534709, 325164 5534732, 325164 5534740, 325162 5534746, 325159 5534749, 325144 5534760, 325143 5534763, 325145 5534782, 325162 5534800, 325184 5534812, 325187 5534815, 325190 5534831, 325196 5534852, 325205 5534867, 325214 5534876, 325219 5534878, 325239 5534880, 325251 5534890, 325259 5534892, 325282 5534887, 325294 5534883, 325314 5534864)")
      .test();
  }
  public void testFloatingPrecision12() throws Exception {
    new BufferValidator(
      100,
      "LINESTRING (307468 5557827, 307467 5557842, 307466 5557854, 307463 5557874, 307459 5557889, 307454 5557902, 307447 5557922, 307440 5557944, 307428 5557965, 307417 5557986, 307411 5557996, 307404 5558020, 307398 5558031, 307390 5558056, 307387 5558066, 307384 5558084, 307383 5558093, 307385 5558102, 307389 5558110, 307394 5558116, 307404 5558121, 307421 5558122, 307443 5558121, 307464 5558127, 307486 5558133, 307502 5558142, 307508 5558150)")
      .test();
  }
  public void testFloatingPrecision13() throws Exception {
    new BufferValidator(
      100,
      "LINESTRING (301395 5535820, 301412 5535803, 301416 5535798, 301420 5535786, 301423 5535782, 301427 5535778, 301432 5535771, 301437 5535768, 301444 5535763, 301447 5535760, 301452 5535757, 301459 5535754, 301462 5535753, 301468 5535750, 301473 5535747, 301481 5535742, 301487 5535739, 301494 5535734, 301499 5535730, 301508 5535725, 301514 5535721, 301521 5535716, 301527 5535714, 301533 5535711, 301538 5535707, 301542 5535703, 301554 5535693, 301559 5535688, 301563 5535681)")
      .test();
  }
  public void testFloatingPrecision14() throws Exception {
    new BufferValidator(
      100,
      "LINESTRING (331384 5535032, 331397 5535031, 331415 5535025, 331426 5535022, 331436 5535016, 331451 5534999, 331460 5534994, 331468 5534993, 331474 5534996, 331479 5535001, 331482 5535015, 331486 5535019, 331493 5535018, 331504 5535011, 331508 5535011, 331519 5535022, 331526 5535023, 331542 5535019, 331547 5535016, 331549 5534994, 331558 5534975, 331562 5534968, 331565 5534966, 331571 5534967, 331575 5534970, 331576 5534978, 331575 5534987, 331568 5535005, 331564 5535022, 331565 5535030, 331570 5535038, 331578 5535044, 331582 5535046, 331592 5535046, 331602 5535043, 331613 5535038, 331631 5535027, 331640 5535021, 331645 5535020, 331654 5535020, 331662 5535022, 331669 5535028, 331676 5535040, 331674 5535065, 331668 5535089, 331659 5535108, 331655 5535118, 331655 5535123, 331662 5535143, 331662 5535147, 331651 5535167, 331646 5535181, 331642 5535198, 331640 5535210, 331641 5535234, 331642 5535245, 331645 5535255, 331648 5535276, 331651 5535287, 331663 5535309, 331665 5535316, 331666 5535324, 331671 5535337, 331677 5535344)")
      .test();
  }

  public void testQuickPolygonUnion() {
    Geometry a = read("POLYGON((0 0, 100 0, 100 100, 0 100, 0 0))");
    Geometry b = read("POLYGON((50 50, 150 50, 150 150, 50 150, 50 50))");
    Geometry[] polygons = new Geometry[] {a, b};
    GeometryCollection polygonCollection = new GeometryFactory().createGeometryCollection(polygons);
    Geometry union = polygonCollection.buffer(0);
    //System.out.println(union);
    assertEquals("POLYGON ((0 0, 0 100, 50 100, 50 150, 150 150, 150 50, 100 50, 100 0, 0 0))", union.toString());
  }
  
  /**
   * This now works since buffer ring orientation is changed to use signed-area test.
   */
  public void testBowtiePolygonLargestAreaRetained() {
    Geometry a = read("POLYGON ((10 10, 50 10, 25 35, 35 35, 10 10))");
    Geometry result = a.buffer(0);
    Geometry expected = read("POLYGON ((10 10, 30 30, 50 10, 10 10))");
    checkEqual(expected, result);
  }
  
  /**
   * This now works since buffer ring orientation is changed to use signed-area test.
   */
  public void testBowtiePolygonHoleLargestAreaRetained() {
    Geometry a = read("POLYGON ((0 40, 60 40, 60 0, 0 0, 0 40), (10 10, 50 10, 25 35, 35 35, 10 10))");
    Geometry result = a.buffer(0);
    Geometry expected = read("POLYGON ((0 40, 60 40, 60 0, 0 0, 0 40), (10 10, 50 10, 30 30, 10 10))");
    checkEqual(expected, result);
  }
  
  /**
   * Following tests check "inverted ring" issue.
   * https://github.com/locationtech/jts/issues/472
   */

  public void testPolygon4NegBufferEmpty() {
    String wkt = "POLYGON ((666360.09 429614.71, 666344.4 429597.12, 666358.47 429584.52, 666374.5 429602.33, 666360.09 429614.71))";
    checkBufferPolygonEmpty(wkt, -9, false);
    checkBufferPolygonEmpty(wkt, -10, true);
    checkBufferPolygonEmpty(wkt, -15, true);
    checkBufferPolygonEmpty(wkt, -18, true);
  }

  public void testPolygon5NegBufferEmpty() {
    String wkt = "POLYGON ((6 20, 16 20, 21 9, 9 0, 0 10, 6 20))";
    checkBufferPolygonEmpty(wkt, -8, false);
    checkBufferPolygonEmpty(wkt, -8.6, true);
    checkBufferPolygonEmpty(wkt, -9.6, true);
    checkBufferPolygonEmpty(wkt, -11, true);
  }

  public void testPolygonHole5BufferNoHole() {
    String wkt = "POLYGON ((-6 26, 29 26, 29 -5, -6 -5, -6 26), (6 20, 16 20, 21 9, 9 0, 0 10, 6 20))";
    checkBufferHasHole(wkt, 8, true);
    checkBufferHasHole(wkt, 8.6, false);
    checkBufferHasHole(wkt, 9.6, false);
    checkBufferHasHole(wkt, 11, false);
  }

  /**
   * Tests that an inverted ring curve in an element of a MultiPolygon is removed
   */
  public void testMultiPolygonElementRemoved() {
    String wkt = "MULTIPOLYGON (((30 18, 14 0, 0 13, 16 30, 30 18)), ((180 210, 60 50, 154 6, 270 40, 290 130, 250 190, 180 210)))";
    checkBufferNumGeometries(wkt, -9, 2);
    checkBufferNumGeometries(wkt, -10, 1);
    checkBufferNumGeometries(wkt, -15, 1);
    checkBufferNumGeometries(wkt, -18, 1);
  }

  /**
   * Checks a bug in the inverted-ring-removal heuristic.
   * See https://github.com/locationtech/jts/issues/876
   */
  public void testLineClosedNoHole() {
    String wkt = "LINESTRING (-20 0, 0 20, 20 0, 0 -20, -20 0)";
    checkBufferHasHole(wkt, 70, false);
  }
  
  public void testSmallPolygonNegativeBuffer_1() {
    String wkt = "MULTIPOLYGON (((833454.7163917861 6312507.405413097, 833455.3726665961 6312510.208920742, 833456.301153878 6312514.207390314, 833492.2432584754 6312537.770332065, 833493.0901320165 6312536.098774815, 833502.6580673696 6312517.561360772, 833503.9404352929 6312515.0542803425, 833454.7163917861 6312507.405413097)))";
    checkBuffer(wkt, -3.8, 
        "POLYGON ((833459.9671068499 6312512.066918822, 833490.7876785189 6312532.272283619, 833498.1465258132 6312517.999574621, 833459.9671068499 6312512.066918822))");
    checkBuffer(wkt, -7, 
        "POLYGON ((833474.0912127121 6312517.50004999, 833489.5713439264 6312527.648521655, 833493.2674441456 6312520.479822435, 833474.0912127121 6312517.50004999))");
  }
  
  public void testSmallPolygonNegativeBuffer_2() {
    String wkt = "POLYGON ((182719.04521570954238996 224897.14115349075291306, 182807.02887436276068911 224880.64421749324537814, 182808.47314301913138479 224877.25002362736267969, 182718.38701137207681313 224740.00115247094072402, 182711.82697281913715415 224742.08599378637154587, 182717.1393717635946814 224895.61432328051887453, 182719.04521570954238996 224897.14115349075291306))";
    checkBuffer(wkt, -5, 
        "POLYGON ((182717 224746.99999999997, 182722.00000000003 224891.5, 182801.99999999997 224876.49999999997, 182717 224746.99999999997))");
    checkBuffer(wkt, -30, 
        "POLYGON ((182745.07127364463 224835.32741176756, 182745.97926048582 224861.56823147752, 182760.5070499446 224858.844270954, 182745.07127364463 224835.32741176756))");
  }
  
  /**
   * See GEOS PR https://github.com/libgeos/geos/pull/978
   */
  public void testDefaultBuffer() {
    Geometry g = read("POINT (0 0)").buffer(1.0);
    Geometry b = g.getBoundary();
    Coordinate[] coords = b.getCoordinates();
    assertEquals(33, coords.length);
    assertEquals(coords[0].x, 1.0);
    assertEquals(coords[0].y, 0.0);
    assertEquals(coords[8].x, 0.0);
    assertEquals(coords[8].y, -1.0);
    assertEquals(coords[16].x, -1.0);
    assertEquals(coords[16].y, 0.0);
    assertEquals(coords[24].x, 0.0);
    assertEquals(coords[24].y, 1.0);
  }
  
  public void testRingStartSimplified() {
    checkBuffer("POLYGON ((200 300, 200 299.9999, 350 100, 30 40, 200 300))",
        20, bufParamRoundMitre(5),
        "POLYGON ((198.88 334.83, 385.3 86.27, -12.4 11.7, 198.88 334.83))"
        );
  }
  
  public void testRingEndSimplified() {
    checkBuffer("POLYGON ((200 300, 350 100, 30 40, 200 299.9999, 200 300))",
        20, bufParamRoundMitre(5),
        "POLYGON ((198.88 334.83, 385.3 86.27, -12.4 11.7, 198.88 334.83))"
        );
  }
  
  //See https://github.com/libgeos/geos/issues/1125
  public void testPolygonQS_1KeepHoles() {
    String wkt = "POLYGON ((-148960.40471687025 6579513.659178475, -148928.0396648308 6581180.350337651, -148245.02968781325 6581451.972854649, -147792.6996258908 6581746.752358279, -147401.9935929195 6581835.62069549, -147210.88713646092 6581715.340847257, -147165.89525315163 6581718.457436403, -147051.42047418532 6581994.824000333, -146383.20456596403 6582271.6080921125, -145714.98865774274 6581994.824000333, -145614.3328386122 6581751.819356657, -145289.96567509088 6581774.313783639, -145251.8481557473 6581761.53544856, -144930.85553391272 6582023.429998221, -144801.3009350866 6582062.324487534, -144649.32383366008 6582200.989144345, -144370.39842075115 6582625.956584843, -144433.62830795025 6582865.17530117, -144245.21925823047 6582676.803607282, -144190.80928273257 6582650.46537934, -143612.84111259945 6583156.449299466, -142975.83638477212 6583147.740058781, -141662.4868972855 6583201.722177097, -141223.96080593477 6583368.877966974, -140134.42107956257 6583317.889953656, -137814.4023306831 6583217.287998203, -136584.74750088027 6583197.764287264, -135440.77723176708 6583205.617216827, -135255.13229718836 6583263.689916569, -135023.58923930765 6582294.773113748, -135333.8024011954 6581864.155949903, -135352.36464941013 6581737.1950687375, -134265.49708919087 6581601.806132041, -134171.3524065637 6581457.461614907, -135439.24541688355 6581616.609798272, -135680.42426915234 6581646.652426795, -136232.904152299 6581417.807766258, -136858.69365364907 6581677.018264907, -136920.3618201151 6581825.898388756, -138060.72504741614 6581967.966335978, -138065.18112161517 6581962.887387468, -138068.72528765528 6581957.509991399, -137927.47673898126 6581707.240832043, -137979.72909569443 6581677.750304447, -138121.13852736415 6581928.304521968, -138125.10691505377 6581928.045287478, -138155.67610065715 6581921.763837175, -138161.590592191 6581925.661995275, -138170.6137036581 6581926.915151091, -138241.9787318571 6581930.376680339, -138796.47362927152 6581702.315792638, -138854.79457708073 6581727.268957612, -138922.92404225498 6581954.795898342, -138946.8159847242 6581964.41530986, -139938.37021221558 6581949.812286194, -139952.45944417492 6581955.406612265, -140868.0275192552 6581942.000814841, -140881.49168103898 6581925.799237404, -141370.63532938264 6581378.75940714, -140611.15578408277 6581049.475858836, -139690.6039836331 6580651.2692048885, -140701.38102529195 6580840.959081494, -141466.3464917169 6581171.3088895, -141779.4716755202 6581271.496093299, -142011.00981524787 6581635.151657183, -142357.03529671414 6581674.463083764, -142838.53214884375 6581404.193874537, -142879.22310559888 6581262.5084437225, -142930.87216570028 6580987.212411177, -143534.5331121017 6580882.981201922, -143430.70803588207 6581258.626695622, -143473.24118656976 6581352.886717385, -143492.18998812925 6581960.431922508, -144142.78527346236 6582035.092768761, -144146.60338361078 6581637.428135656, -144191.08020551776 6581601.260220021, -144537.44044373164 6581705.61685815, -144590.74628867855 6581606.573066762, -144763.58673427685 6581465.554340523, -144954.41934581465 6580944.824934003, -145151.23516586225 6581077.603417352, -145568.85455018 6581011.191128295, -145714.98865774274 6580658.3921838915, -146383.20456596403 6580381.6080921125, -147120.06182660736 6580824.107067849, -148440.1510719309 6579967.272995219, -148960.40471687025 6579513.659178475), (-143109.908378959 6582619.958897926, -142984.1537920762 6583123.483449154, -143587.23572115527 6583134.1575482, -143327.76122149464 6582574.317202426, -143109.908378959 6582619.958897926), (-141937.54728911287 6582399.340431561, -141701.905174685 6583118.585429914, -142423.06886593942 6583134.765523819, -142134.0212542649 6582436.348369016, -141937.54728911287 6582399.340431561), (-136955.57202899482 6581910.903352568, -137006.19392140477 6582033.115411777, -137716.5705505733 6582951.997047175, -138024.97013314272 6582044.130371629, -136955.57202899482 6581910.903352568))";
    Polygon geom = (Polygon) read(wkt);
    Polygon buf = (Polygon) geom.buffer(0.00001, 1);
    assertEquals(geom.getNumInteriorRing(), buf.getNumInteriorRing());
  }
  
  //See https://github.com/libgeos/geos/issues/1223
  public void testRingHoleEroded() {
    String wkt = "LINESTRING (25 44, 31 44, 32 38, 29 37, 25 37, 25 38, 24 40, 24 44, 25 44)";
    checkBuffer(wkt, 100, 
"POLYGON ((50.95 141.99, 70.09 136.04, 87.66 126.4, 102.96 113.44, 115.36 97.69, 124.38 79.78, 129.64 60.44, 130.64 54.44, 131.93 34.31, 129.16 14.34, 122.44 -4.68, 112.03 -21.96, 98.37 -36.8, 82.02 -48.59, 63.62 -56.87, 60.62 -57.87, 45.02 -61.71, 29 -63, 25 -63, 4.33 -60.84, -15.44 -54.46, -33.47 -44.12, -48.97 -30.29, -61.28 -13.55, -69.87 5.38, -70.87 8.38, -74.71 23.98, -76 40, -76 44, -74.08 63.51, -68.39 82.27, -59.15 99.56, -46.71 114.71, -31.56 127.15, -14.27 136.39, 4.49 142.08, 24 144, 31 144, 50.95 141.99))");
    checkBuffer(wkt, 10,
        "POLYGON ((15.06 35.53, 14.27 37.7, 14 40, 14 44, 14.19 45.95, 14.76 47.83, 15.69 49.56, 16.93 51.07, 18.44 52.31, 20.17 53.24, 22.05 53.81, 24 54, 31 54, 32.99 53.8, 34.91 53.2, 36.67 52.24, 38.2 50.94, 39.44 49.37, 40.34 47.58, 40.86 45.64, 41.86 39.64, 41.99 37.63, 41.72 35.63, 41.04 33.73, 40 32, 38.64 30.52, 37 29.34, 35.16 28.51, 32.16 27.51, 30.6 27.13, 29 27, 25 27, 23.05 27.19, 21.17 27.76, 19.44 28.69, 17.93 29.93, 16.69 31.44, 15.76 33.17, 15.19 35.05, 15.17 35.31, 15.06 35.53))");
    checkBuffer(wkt, 2,
        "POLYGON ((31.4 45.96, 31.78 45.84, 32.13 45.65, 32.44 45.39, 32.69 45.07, 32.87 44.72, 32.97 44.33, 33.97 38.33, 34 37.93, 33.94 37.53, 33.81 37.15, 33.6 36.8, 33.33 36.5, 33 36.27, 32.63 36.1, 29.63 35.1, 29.32 35.03, 29 35, 25 35, 24.61 35.04, 24.23 35.15, 23.89 35.34, 23.59 35.59, 23.34 35.89, 23.15 36.23, 23.04 36.61, 23 37, 23 37.53, 22.21 39.11, 22.05 39.54, 22 40, 22 44, 22.04 44.39, 22.15 44.77, 22.34 45.11, 22.59 45.41, 22.89 45.66, 23.23 45.85, 23.61 45.96, 24 46, 31 46, 31.4 45.96), (26 40.47, 26.74 39, 28.68 39, 29.75 39.36, 29.31 42, 26 42, 26 40.47))");
  }
  
  // Checks that a CCW ring generates a correct buffer
  // see https://github.com/libgeos/geos/issues/1236
  public void testRingCCW() {
    String wkt = "LINEARRING (-0.25 0.25, -0.25 0.75, -0.75 0.75, -0.75 0.25, -0.25 0.25)";
    checkBuffer(wkt, 1, 
"POLYGON ((0.73 0.05, 0.67 -0.13, 0.58 -0.31, 0.46 -0.46, 0.31 -0.58, 0.13 -0.67, -0.05 -0.73, -0.25 -0.75, -0.75 -0.75, -0.95 -0.73, -1.13 -0.67, -1.31 -0.58, -1.46 -0.46, -1.58 -0.31, -1.67 -0.13, -1.73 0.05, -1.75 0.25, -1.75 0.75, -1.73 0.95, -1.67 1.13, -1.58 1.31, -1.46 1.46, -1.31 1.58, -1.13 1.67, -0.95 1.73, -0.75 1.75, -0.25 1.75, -0.05 1.73, 0.13 1.67, 0.31 1.58, 0.46 1.46, 0.58 1.31, 0.67 1.13, 0.73 0.95, 0.75 0.75, 0.75 0.25, 0.73 0.05))");
  }
  
  // Checks that a skinny element polygon is eroded with no internal predicision reduction due to topo exes
  // see https://github.com/libgeos/geos/issues/1182
  public void testElementErodedEx() {
    String wkt = "MULTIPOLYGON (((48268.99938 -49048.29324, 44429.1 -55700.232847, 44429.1 -55107.317582, 44506.1 -54974, 44429.1 -54840, 44429.1 -51569.2, 42170.10515 -49316.27944, 48268.99938 -49048.29324)), ((43433.08324 -51823.15037, 42480.09977 -55494.96132, 42477.638798 -55504.400121, 42480.20715 -55494.547587, 42482.247919 -55485.931009, 42482.431666 -55485.976608, 43433.08324 -51823.15037)))";
    checkBuffer(wkt, -1, 
"POLYGON ((48267.2218198241 -49049.37231112561, 44430.1 -55696.500291383236, 44430.1 -55107.58561879013, 44506.96594366424 -54974.50014155032, 44507.05088958367 -54974.309530288774, 44507.09442572395 -54974.10543945913, 44507.09465615313 -54973.896756903174, 44507.05157083636 -54973.69257042533, 44506.96704607295 -54973.50177203271, 44430.1 -54839.73314639927, 44430.1 -51569.2, 44430.08071956347 -51569.00457958696, 44430.02362172434 -51568.81669475566, 44429.93090822513 -51568.64359050924, 44429.80615417933 -51568.49194189856, 42172.42282165639 -49317.178566110095, 48267.2218198241 -49049.37231112561))");    
  }
  
  /**
   * Test a buffer-by-zero case which revealed a bug in the DeptSegment comparator.
   * See https://github.com/locationtech/jts/issues/1131
   */
  public void testBufferByZeroKeepsAllElements() {
    String wkt = "MULTIPOLYGON (((24 95.239, 24 96, 24 99, 24.816 99, 24 95.239)), ((3 90, 3 93, 3 96, 3 99, 21 99, 21 96, 21 93, 21 90, 3 90)))";
    checkBuffer(wkt, 0, 
"MULTIPOLYGON (((24 95.239, 24 96, 24 99, 24.816 99, 24 95.239)), ((3 90, 3 93, 3 96, 3 99, 21 99, 21 96, 21 93, 21 90, 3 90)))");    
  }
  
  //-- see https://github.com/libgeos/geos/issues/1321
  public void testArtifactsRemovedFromLineBuffer() {
    String wkt = "LINESTRING (29.979984761 71.879095724, 29.445974092 73.02148841, 29.002794006 73.679857725, 28.952197105 73.745389857, 28.676962154 74.089814544)";
    checkBufferNumGeometries(wkt, 100, 1);
  }
  
  //-- see https://github.com/r-spatial/sf/issues/2552
  public void testArtifactsRemovedFromLineBufferFlatEnd() {
    String wkt = "LINESTRING (245184.6 6045650, 245193.3 6045649, 245201.7 6045651, 245204.3 6045653)";
    Geometry geom = read(wkt);
    Geometry buf = BufferOp.bufferOp(geom, 50, bufParamEndCapFlat());
    assertEquals(1, buf.getNumGeometries());
  }
  
  //--------------------------------
  
  public void testInvalidCoordPoint() {
    // works for Inf ordinates as well
    Geometry geom = read("POINT (NaN NaN)");
    checkBufferPolygonEmpty(geom, 1, true);
  }

  public void testInvalidCoordsLine() {
    // works for Inf ordinates as well
    Geometry geom = read("LINESTRING (Inf Inf, NaN NaN)");
    checkBufferPolygonEmpty(geom, 1, true);
  }
  
  public void testInvalidCoordShell() {
    // using Inf ordinates creates a valid ring with equal endpoints
    Geometry geom = (Polygon) read("POLYGON ((Inf Inf, Inf Inf, Inf Inf, Inf Inf, Inf Inf))");
    checkBufferPolygonEmpty(geom, 1, true);
  }
  
  public void testInvalidCoordHole() {
    // using Inf ordinates creates a valid ring with equal endpoints
    Polygon poly = (Polygon) read("POLYGON ((1 9, 9 9, 9 1, 1 1, 1 9), (3 7, 7 7, 7 3, 3 3, 3 7))");
    Polygon polyInfHole = (Polygon) read("POLYGON ((1 9, 9 9, 9 1, 1 1, 1 9), (3 7, 7 7, 7 3, 3 3, 3 7), (Inf Inf, Inf Inf, Inf Inf, Inf Inf, Inf Inf))");

    Geometry bufferOrig = poly.buffer(1);
    Geometry bufferInf = polyInfHole.buffer(1);
    // buffers should be same since inf hole is dropped
    checkEqual(bufferOrig, bufferInf);
  }
  
  //===================================================
  
  private static Coordinate[] infCoords(int size) {
    Coordinate[] coords = new Coordinate[size];
    for (int i = 0; i < size; i++) {
      coords[i] = new Coordinate(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
    }
    return coords;
  }
  
  private static BufferParameters bufParamRoundMitre(double mitreLimit) {
    BufferParameters param = new BufferParameters();
    param.setJoinStyle(BufferParameters.JOIN_MITRE);
    param.setMitreLimit(mitreLimit);
    return param;
  }
  
  private static BufferParameters bufParamEndCapFlat() {
    BufferParameters param = new BufferParameters();
    param.setEndCapStyle(BufferParameters.CAP_FLAT);
    return param;
  }
  
  private void checkBuffer(String wkt, double dist, BufferParameters param, String wktExpected) {
    Geometry geom = read(wkt);
    Geometry result = BufferOp.bufferOp(geom, dist, param);
    Geometry expected = read(wktExpected);
    checkEqual(expected, result, 0.01);
  }
  
  private void checkBuffer(String wkt, double dist, String wktExpected) {
    Geometry geom = read(wkt);
    Geometry result = BufferOp.bufferOp(geom, dist);
    Geometry expected = read(wktExpected);
    checkEqual(expected, result, 0.01);
  }
  
  private void checkBufferPolygonEmpty(String wkt, double dist, boolean isEmptyExpected) {
    Geometry a = read(wkt);
    checkBufferPolygonEmpty(a, dist, isEmptyExpected);
  }

  private void checkBufferPolygonEmpty(Geometry geom, double dist, boolean isEmptyExpected) {
    Geometry result = geom.buffer(dist);
    assertTrue(result instanceof Polygon);
    assertTrue(isEmptyExpected == result.isEmpty());
  }

  private void checkBufferHasHole(String wkt, double dist, boolean isHoleExpected) {
    Geometry a = read(wkt);
    Geometry result = a.buffer(dist);
    assertTrue(isHoleExpected == hasHole(result));
  }

  private void checkBufferNumGeometries(String wkt, double dist, int numExpected) {
    Geometry a = read(wkt);
    Geometry result = a.buffer(dist);
    assertEquals(numExpected, result.getNumGeometries());
  }

  private boolean hasHole(Geometry geom) {
    for (int i = 0; i < geom.getNumGeometries(); i++) {
      Polygon poly = (Polygon) geom.getGeometryN(i);
      if (poly.getNumInteriorRing() > 0) return true;
    }
    return false;
  }



}
